#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>

#include <iostream>
#include <string>
#include <map>
#include <list>


extern "C" {
#include <pcap.h>
}

using namespace std;

#include "ip.h"
#include "dns.h"
#include "prio.h"
#include "monlist.h"

ostream& operator<<(ostream &os, struct timeval tv) 
{
	struct tm *ti = localtime(&tv.tv_sec);

	os << ti->tm_hour << ":" << ti->tm_min << ":" << ti->tm_sec << "." << tv.tv_usec;
	return os;
}

CRecord::CRecord(CRecord *cr)
{
	port_src_m = cr->port_src_m;
	port_dst_m = cr->port_dst_m;
	memcpy(&lastUpdate_, &cr->lastUpdate_, sizeof (lastUpdate_));
	memcpy(&lastUpdate_, &cr->lastUpdate_, sizeof (firstUpdate_));
	if (cr->llSrc_m != 0) {
		llSrc_m = new char[strlen(cr->llSrc_m) + 1];
		//printf("allocated llSrc %x\n", llSrc_m);
		strcpy(llSrc_m, cr->llSrc_m);
	} else {
		llSrc_m = 0;
	}
	if (cr->llDst_m != 0) {
		llDst_m = new char[strlen(cr->llDst_m) + 1];
		// printf("allocated llDst %x\n", llDst_m);
		strcpy(llDst_m, cr->llDst_m);
	} else {
		llDst_m = 0;
	}

	srcSerial_m = cr->srcSerial_m;
	dstSerial_m = cr->dstSerial_m;
	ip_src_m = cr->ip_src_m;
	ip_dst_m = cr->ip_dst_m;
	ip_proto_m = cr->ip_proto_m;
	volume_m = cr->volume_m;
	packets_m = cr->packets_m;

	data_ += cr->data_;

	char	src_ip_char[inet_to_stringSize];
	inet_to_char(ip_src_m, src_ip_char);
	char	dst_ip_char[inet_to_stringSize];
	inet_to_char(ip_dst_m, dst_ip_char);
	key_ = string(src_ip_char)
				+ string(dst_ip_char)
				+ port_to_string(port_src_m)
				+ port_to_string(port_dst_m)
				+ protocol_to_string(ip_proto_m);
}

CRecord::CRecord(const struct ip *ip, int port_src, int port_dst,
				int volume, const char *llSrc, const char *llDst, string data)
		: ip_src_m(ip->ip_src), srcSerial_m(-1), dstSerial_m(-1), ip_dst_m(ip->ip_dst),
				ip_proto_m(ip->ip_p), volume_m(volume), packets_m(1), data_(data)
{
	port_src_m =	ntohs(port_src);
	port_dst_m =	ntohs(port_dst);
	memset(&lastUpdate_, 0, sizeof (lastUpdate_));
	memset(&lastUpdate_, 0, sizeof (firstUpdate_));
	if (llSrc != 0) {
		llSrc_m = new char[strlen(llSrc) + 1];
		// printf("allocated llSrc %x\n", llSrc_m);
		strcpy(llSrc_m, llSrc);
	} else {
		llSrc_m = 0;
	}
	if (llDst != 0) {
		llDst_m = new char[strlen(llDst) + 1];
		// printf("allocated llDst %x\n", llDst_m);
		strcpy(llDst_m, llDst);
	} else {
		llDst_m = 0;
	}
	char	src_ip_char[inet_to_stringSize];
	inet_to_char(ip_src_m, src_ip_char);
	char	dst_ip_char[inet_to_stringSize];
	inet_to_char(ip_dst_m, dst_ip_char);
	key_ = string(src_ip_char)
				+ string(dst_ip_char)
				+ port_to_string(port_src_m)
				+ port_to_string(port_dst_m)
				+ protocol_to_string(ip_proto_m);
}

CRecord::~CRecord()
{
	if (llSrc_m != 0) {
		// printf("delete llSrc %x\n", llSrc_m);
		delete [] llSrc_m;
	}
	if (llDst_m != 0) {
		 //printf("delete llDst %x\n", llDst_m);
		delete [] llDst_m;
	}
}


ostream& operator<<(ostream &os, const CRecord *cr) {
	char	src_ip_char[inet_to_stringSize];
	inet_to_char(cr->ip_src_m, src_ip_char);
	char	dst_ip_char[inet_to_stringSize];
	inet_to_char(cr->ip_dst_m, dst_ip_char);

	os << "src " << src_ip_char
		<< " ser " << cr->srcSerial_m
		<< " dst " << dst_ip_char
		<< " ser " << cr->dstSerial_m
		<< " sprt " << cr->port_src_m
		<< " dprt " << cr->port_dst_m
		<< " prot " << (unsigned int)cr->ip_proto_m
		<< " vol " << cr->volume_m;
	if (cr->packets_m == 1) {
		os << " " << cr->lastUpdate_
			<< endl;
	} else {
		if (cr->lastUpdate_.tv_sec != cr->firstUpdate_.tv_sec)
			os << " ovr " << cr->lastUpdate_.tv_sec - cr->firstUpdate_.tv_sec;
		else
			os << " ovr " << cr->lastUpdate_.tv_usec - cr->firstUpdate_.tv_usec << " usec ";
		os << " paks " << cr->packets_m
			<< " " << cr->firstUpdate_
			<< " " << cr->lastUpdate_
			<< endl;
	}
	return os;
}

void	CRecord::Print(ostream &os) {
	os << this;
}


RTable::RTable(int SleepTime, DnsCache *dnsCache, int age, int fullFlushInterval, char *dev)
			: SleepTime_m(SleepTime), dnsCache_m(dnsCache), age_(age),
			fullFlushInterval_(fullFlushInterval),
			interfaceName_m(dev) {
	descr_m = 0;
	dataLinkType_m = 0;

	time_t  ti;
	time(&ti);
	if (fullFlushInterval != 0)
		nextFlush_ = ti - (ti % fullFlushInterval) + fullFlushInterval;
	else
		nextFlush_ = 0;
}

void	RTable::Set_tcapInfo(pcap_t *descr) {
	descr_m = descr;
	dataLinkType_m = pcap_datalink(descr);
}
